package com.example.spring_demo;

import com.example.spring_demo.config.UserInfo;
import com.example.spring_demo.controller.UserController;
import com.example.spring_demo.di.TestController;
import com.example.spring_demo.service.UserService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
//@ComponentScan("com.example.spring_demo") SpirngBoot启动时会默认扫描当前目录及其子目录下的所有类，所以不需要再配置@ComponentScan
public class SpringDemoApplication {

    public static void main(String[] args) {
        //Spring上下文
        ApplicationContext context = SpringApplication.run(SpringDemoApplication.class, args);
        //Spring工厂获取Bean
        UserController userController = context.getBean(UserController.class);
        //调用方法
        userController.doController();


        //获取Bean的三种方式
        //1、通过类的类型
        //2、通过Bean的名称
        //3、通过类的类型和名称


        //方式1
        UserService userService = context.getBean(UserService.class);
        userService.doService();

        //方式2
        //Spring工厂创建的Bean，Bean命名默认为小驼峰命名
        //特殊情况：如果类名前两位都为大写，bean的名称为类名
        UserService userService1 = (UserService) context.getBean("userService");
        userService1.doService();


        //方式3
        UserService userService2 =  context.getBean(UserService.class, "userService");
        userService2.doService();

        /*
         * ApplicationContext和BeanFactory的区别
         * 1、继承关系和功能方面来说：Spring容器有两个顶级的接口：BeanFactory和ApplicationContext。
         * 其中BeanFactory提供了基础的访问容器的能力，而ApplicationContext属于BeanFactory的子接口，它除了继承了BeanFactory的所有功能之外，它还拥有独特的特性，还添加了：
         * 对国际化支持、资源访问支持以及事件传播等方面的支持。
         * 2、从性能方面来说：
         * ApplicationContext-》饿汉模式，启动时加载并初始化所有的Bean对象，启动慢，但是后续获取Bean对象速度快
         * BeanFactory-》懒汉模式，启动时不加载Bean对象，获取Bean对象时才加载，启动快，但是后续获取Bean对象速度慢
         */

        /*
         * 五大注解：
         * @Controllr -》控制器注解，标识一个类是一个控制器类，可以处理HTTP请求
         *
         * @Service -》 服务注解，标识一个类是一个服务类，可以提供业务逻辑
         *
         * @Repository -》 仓库注解，标识一个类是一个仓库类，可以访问数据库
         *
         * @Component -》 (组件存储）通用注解，标识一个类是一个通用类，可以被Spring管理
         *
         * @Configuration -》 配置注解，标识一个类是一个配置类，可以用来配置Spring的各种功能
         *
         * 几个注解之间的关系：
         * @Component衍生出了其他四个注解
         * 如果想被外界访问到，只能使用@Controller
         */

        /*
         * 方法注解@Bean
         * 必须搭配五大注解来使用
         * 应用场景：
         * 1、如果引入一个第三方的jar包，也希望交给Spring管理，是没有办法加五大注解的
         * 2、需要对于一个类定义多个对象时，比如数据库操作，定义多个数据源，也是没有办法通过五大注解创建的
         *
         * 使用@Bean注解，Bean的名称是方法名
         * 一个类型，存在多个bean时，不可以仅采用类型来获取对象
         * 注意：
         * 1、@Bean注解修饰的方法，不能添加参数，仅支持在方法内部完成初始化。
         * 2、@Bean注解修饰的方法，只能有一个返回值，不能返回null。
         * 3、@Bean注解修饰的方法，只能在类上使用，不能在方法上使用。
         * 4、@Bean注解修饰的方法，可以添加@Scope注解，来指定Bean的作用域。
         * 5、如果需要的Bean的类型，对应的对象只有一个时，就直接赋值，如果有多个时，通过名称去匹配获取
         * 6、@Bean注解修饰的方法，可以添加@Primary注解，来指定Bean的优先级。
         */
        //登录代码


        //@Bean演示
        UserInfo userInfo = (UserInfo) context.getBean("userInfo2");
        System.out.println(userInfo.toString());

        UserInfo userInfo1 = (UserInfo) context.getBean("userInfo",UserInfo.class);
        System.out.println(userInfo1.toString());


        TestController testController = context.getBean(TestController.class);
        testController.doTestController3();
        /*System.out.println(testController.getUserInfo().toString());*/


    }

}
